Passa al contenuto principale

Esercitazione 6

Esercizio 6.1: esame 2024-07-16

Qui testo e soluzione.

Provare da sé

Provare a svolgere da sé l'esercizio, prima di guardare la soluzione o andare oltre per la discussione.

Esercizi senza sintesi di reti combinatorie

Non tutti gli esercizi includono la sintesi di reti combinatorie, così come non tutti i pretest, esercizi di Assembler o domande all'orale coprono un dato argomento del corso.

Nel complesso, ogni esame ambisce a coprire tutti gli argomenti del corso.

L'aspetto particolare di questo esercizio è la richiesta di utilizzare microsottoprogrammi. Questo significa codificare una serie di stati a cui si può saltare da diversi altri stati, a cui poi si intende tornare, proprio come i sottoprogrammi nel software. Al posto di call e ret, però, si usa il registro MJR (Multiway Jump Register).

La struttura per la descrizione è la seguente. Siano S0...SN gli stati della sequenza principale, e siano Smp0...SmpN gli stati del microsottoprogramma. Uno stato della sequenza principale può saltare ad un microsottoprogramma così

    S0: begin
...
MJR <= S1;
STAR <= Smp0;
end

Al termine del microsottoprogramma si salterà poi alla sequenza principale così

    SmpN: begin
...
STAR <= MJR;
end

All'interno del processore sEP8 visto nel corso si fa un uso massiccio di microsottoprogrammi per separare le varie operazioni del processore in sequenze generiche, atomiche e ben riconoscibili, come la fase di esecuzione di una specifica istruzione, l'accesso ad una locazione di memoria, l'accesso ad una interfaccia di I/O, etc.

Nella soluzione di questo esercizio, si usa un microsottoprogramma che esegue l'handshake e preleva un dato dal convertitore A/D. Il flusso principale non fa che saltare a questo microsottoprogramma 3 volte per prelevare i dati di cui ha bisogno. In S3, il calcolo è solo descritto.

...
always @(posedge clock) if(reset_ == 1) #3 begin
casex (STAR)
S0: begin
OUT <= 0;
MJR <= S1;
STAR <= S_read0;
end
S1: begin
X2 <= X0;
MJR <= S2;
STAR <= S_read0;
end
S2: begin
X1 <= X0;
MJR <= S3;
STAR <= S_read0;
end
S3: begin
OUT <= ((X0 + X1 + X2) >= 164) ? 1 : 0;
STAR <= S0;
end

// microsottoprogramma per l'acquisizione di un campione
// il dato acquisito viene lasciato in X0
S_read0: begin
SOC <= 1;
STAR <= (eoc == 1'b0) ? S_read1 : S_read0;
end
S_read1: begin
SOC <= 0;
STAR <= (eoc == 1'b1) ? S_read2 : S_read1;
end
S_read2: begin
X0 <= x;
STAR <= MJR;
end
endcase
end

Un altro aspetto critico è come sintetizzare una rete del genere, cioè come si implementa effettivamente dell'hardware che si comporta in questo modo. L'aspetto chiave è il fatto che quando non si usano microsottoprogrammi, i valori assegnati a STAR sono sempre delle costanti, che come abbiamo visto possono essere sintetizzate usando una ROM. I salti che usano MJR invece no, perché, per l'appunto, usano un registro da cui viene letto il prossimo stato.

Va quindi utilizzata una architettura diversa. Una di quelle viste nel corso è così schematizzata.

In questa architettura notiamo che si aggiunge un nuovo filo in uscita alla ROM per distinguere i salti (in)condizionati dai salti che leggono da MJR. Possiamo quindi sintetizzare la parte controllo di questo esercizio con una ROM come la seguente.

// Per utilizzare il registro MJR, va esteso il modello di sintesi della parte controllo e la relativa ROM, in modo da distinguere i salti guidati da MJR e non (salti incondizionati o a due vie).

// Per distinguere questi salti da quelli guidati da MJR, introduciamo un altro multiplexer guidato dal campo m-type della ROM
// Questo varrà 0 per i salti incondizionati o a due vie e 1 per i salti guidati da MJR.

// Per i salti incondizionati o a due vie, si utilizzano i campi m-addr T ed m-addr F della ROM, ed un multiplexer guidato da una delle variabile di condizionamento prodotte dalla parte operativa.
// Dato che, in questo caso, abbiamo una sola variabile di condizionamento, non c'è bisogno di distinguerle tramite un multiplexer ed il campo c_eff della ROM, che quindi omettiamo.

/*
m-addr | m-code | m-addr T | m-addr F | m-type
-------------------------------------------------------------------------
001 (S1) | 000000101 | 100 (S_read0) | 100 (S_read0) | 0
000 (S0) | 100000010 | 100 (S_read0) | 100 (S_read0) | 0
010 (S2) | 010000011 | 100 (S_read0) | 100 (S_read0) | 0
011 (S3) | 000001X00 | 000 (S0) | 000 (S0) | 0
100 (S_read0) | 0001X0000 | 100 (S_read0) | 101 (S_read1) | 0
101 (S_read1) | 000010000 | 110 (S_read2) | 101 (S_read1) | 0
110 (S_read2) | 001000000 | XXX | XXX | 1

*/
Niente salti condizionati con MJR

L'architettura presentata permette solo

  • Salti incodinzionati a stato costante, del tipo STAR <= S0;, da sintetizzare con m-type = 0, c_eff = X, m-true = m-false = S0.
  • Salti condizionati a stati costanti, del tipo STAR <= c1 ? S0 : S1;, da sintetizzare con m-type = 0, c_eff = c1, m-true = S0, m-false = S1.
  • Salti incondizionati a MJR, del tipo STAR <= MJR;, da sintetizzare con m-type = 1, c_eff = X, m-true = X, m-false = X.

Non sono sintetizzabili invece salti del tipo STAR <= c1 ? MJR : S1. Per far questo ci vorrebbe un'altra architettura, diversa da quelle viste in questo corso.

Esercizio 6.2: esame 2024-09-10

Qui testo e soluzione.

Provare da sé

Provare a svolgere da sé l'esercizio, prima di guardare la soluzione o andare oltre per la discussione.

Il primo aspetto interessante di questo esercizio è la ricezione di byte tramite linea seriale, la cui specifica è data dal testo. Dalla specifica, quando un valore viene trasmesso si imposta la linea su 0, la lunghezza dell'intervallo in cui la linea è a 0 ci indica quale bit è stato trasmesso. Ci sono due range dati: [2,7][2, 7] periodi di clock per un bit 1, [11,15][11, 15] per un bit 0.

Assenza di errori

In esercizi d'esame come questo, si assume che non ci siano errori di alcun tipo nel trasmettitore o sulla linea. Non ci saranno quindi periodi di lunghezze diversa dagli intervalli indicati. La distanza tra 7 e 11 permette di escludere ambiguità nella misurazione della lunghezza degli intervalli.

Abbiamo quindi bisogno di due ingredienti. Il primo è un registro su cui campioniamo in shift continuo. Dato che l'esercizio indica i bit inviati a partire dal più signficativo, si può campionare un byte eseguendo 8 volte BYTE <= {nuovo_bit, BYTE[7:1]};. Il secondo ingrediente è aspettare che rxd vada a 0, e poi contare per quanti periodi di clock rimane a 0, per esempio con un registro COUNT. Possiamo quindi calcolare nuovo_bit = (COUNT <= 7);, che è ottimizzabile (ma non è indispensabile) notando che è equivalente a nuovo_bit = ~COUNT[3].

Il secondo aspetto interessante dell'esercizio riguarda il calcolo combinatorio da eseguire con i byte ricevuti. L'esercizio chiede di interpretarli come numeri interi in complemento alla radice x0,...,xnx_0, ..., x_n, calcolandone di volta in volta la somma sis_i che è posta in uscita. Quando tale somma sarà non più rappresentabile, si torna alle condizioni al reset (ossia il prossimo campione sarà x0x_0).

Per far questo utilizziamo iterativamente un sommatore, a cui colleghiamo in ingresso i registri S (inizializzato a 0) e BYTE. Sia s l'uscita di tale sommatore. Ognivolta che un nuovo campione è stato ricevuto completamente, campioniamo la nuova somma con S <= s.

Dato che si parla di numeri interi in complemento alla radice, come criterio di rappresentabilità dobbiamo usare il filo di overflow ow di questo sommatore. L'uscita c_out è invece irrilevante, sarebbe di interesse sole se questi fossero numeri naturali.